A compiler is a program that reads a program written in a first language, i.e., the source language, and translates the first language to an equivalent program in a second language, i.e., a target or machine code language. During the process of compiling a source language into a machine language, the compiler generates an intermediate representation which is then subjected to an optimization action, prior to being output as the machine code. The code optimization phase attempts to improve the intermediate language so that faster executing machine code will result.
During an optimizing action, the compiler may reorder or eliminate the execution of source level expressions. Such reordering or elimination destroys the sequential correspondence between the source statements and the machine code statements, while potentially rendering the machine code into a more efficiently executing program.
Debugging allows a user to control the execution of a program (e.g., by the setting of breakpoints) and to inspect the state of execution of the program at each breakpoint (e.g., to print a current value of a variable). A symbolic debugger is a program wherein all interactions are in terms of the source level language program from which the machine code has been derived. An optimizing compiler makes more difficult the task of the symbolic debugger by complicating the correspondence between the source code and the machine code. Any reordering or elimination of source level expressions complicates the mapping of breakpoints and values in the source code to those in the machine code.
In addition to statement reordering or elimination, an optimizer performs "lifetime shortening" actions with respect to variables used in the program. More particularly, machine code generated by the optimizer will discard the value of a variable as soon as the compiler can see no further need for it--even though it would be useful for a debugger if the compiler would continue to make the variable value available.
In regards to statement reordering/elimination, the debugging of such optimized code is rendered difficult due to the fact that the user is conversant with the sequence of actions of the source code and expects the machine code to operate, with the same sequence of operations.
When debuggers attempt to debug optimized code, they approach the statement re-ordering problem in a relatively simplistic manner. Debuggers generally run the program in the revised order in which the compiler has placed the machine instructions and display to the user that order. The debugger may, in addition provide some information to the user which indicates the fact of a re-ordering at the place in the code where the re-ordering occurred. Such a debugging operation is described in "DOC: a Practical Approach to Source-Level Debugging of Globally Optimized Code," Coutant et al., Proceedings of SIGPLAN 1988, Conference on Programming Language Design and Implementation, Atlanta, Ga., 1988, pgs. 125-134. The prior art has also suggested the use of graphical display techniques to assist a user in tracking the altered execution order of the machine code from the source code.
Notwithstanding such debugging aids, statement reordering/elimination still presents significant difficulties to the user during a debugging operation.
Prior art solutions to variable lifetime shortening have included the following: (i) making a register allocator function lengthen register lifetimes to as long a period as possible; and (ii) causing a debugger to recognize when a variable's lifetime has ended or may have ended and to print a warning of the lifetime end of the variable. The first of these solutions compromises the compiler's ability to optimize the machine code and, at best, only reduces the frequency with which data is lost. It does not prevent the loss of the data. The second solution simply tells the user when the problem arises, but doesn't stop it from arising.
Such solutions are discussed in the following prior art references: "Evicted Variables and the Interaction of Global Register Allocation and Symbolic Debugging", Adl-Tabatabai et al., ACM-20th PoPL, January 1993, pgs. 371-383; "Detection and Recovery of Endangered Variables Caused by Instruction Scheduling", Adl-Tabatabai et al., ACM-SIGPLAN-PLDI, June 1993, pgs. 13-25; and "Debugging Optimized Code Without Being misled", M. Copperman, Report 92-01, May 8, 1992, Board of Studies in Computer and Information Sciences, University of California at Santa Cruz, Calif. 95064.
There is a need for a more efficient method for debugging optimized machine code which avoids the problems created by the discard of variable values and statement reordering or elimination.